#pragma once

#include<iostream>
#include<sstream>
#include<string>
#include<vector>
#include<unordered_map>
#include<algorithm>
#include<unistd.h>
#include<cstdlib>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/socket.h>
#include<sys/sendfile.h>
#include<sys/stat.h>
#include"Util.hpp"
#include"Log.hpp"

#define SEP ": "
#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"
#define PAGE_404 "404.html"

#define OK 200
#define BAD_REQUEST 400
#define NOT_FOUND 404
#define SERVER_ERROR 500

static std::string Code2Desc(int code)
{
  switch(code){
    case 200:
      return "OK";
    case 404:
      return "Not Found";
    default:
      return "";
  }
}

static std::string  Suffxi2Desc(const std::string& suffxi )
{
  static std::unordered_map<std::string,std::string> suffxi2desc = {
    {".html","text/html"},
    {".css","text/css"},
    {".js","application/javascript"},
    {".jpg","application/x-jpg"},
    {".xml","application/xml"}
  }; 
  auto iter = suffxi2desc.find(suffxi);
  if(iter != suffxi2desc.end()){
    return iter->second;
  }
  return "text/html";
}

class HttpRequest{
  public:
    std::string request_line;
    std::vector<std::string> request_header;
    std::string blank;
    std::string request_body;

    //解析完毕之后的结果
    std::string method;
    std::string uri; //path?args
    std::string version;

    std::unordered_map<std::string,std::string> header_kv;
    std::string path;
    std::string suffxi; //后缀
    std::string query_string;
    int content_length = 0;
    
    int size;


    bool cgi;
  public:
    HttpRequest():content_length(0),cgi(false)
    {}

};

class HttpResponse{
  public:
    std::string status_line;
    std::vector<std::string> response_head;
    std::string blank;
    std::string response_body;

    int status_code;
    int fd;
  public:
    HttpResponse():blank(LINE_END),status_code(OK),fd(-1){}
    
};


//读取请求，分析请求，构建响应
//IO通信
class EndPoint{
  private:
    int sock;
    HttpRequest http_request;
    HttpResponse http_response;
    bool stop = false;
  private:
    bool RecvHttpRequestLine()
    {
      auto& line = http_request.request_line;
      if(Util::ReadLine(sock,line)>0){
         line.resize(line.size()-1);
         LOG(INFO,http_request.request_line);
       }
      else{
        stop = true;
      }
      std::cout << "RecvHttpRequestLine: " << stop << std::endl;
      return stop;
    }
    bool RecvHttpRequestHeader()
    {
      std::string line;
      while(true){
        line.clear();
        if(Util::ReadLine(sock,line)<=0){
          stop = true;
          break;
        }
        if(line == "\n"){
          http_request.blank = line;
          break;
        }
        line.resize(line.size()-1);
        http_request.request_header.push_back(line);
        LOG(INFO,line);
      }
      std::cout << "stop debug: " << stop << std::endl;
      return stop;
    }
    void ParseHttpRequestLine()
    {
        auto& line = http_request.request_line;
        std::stringstream ss(line);
        ss >> http_request.method >> http_request.uri >> http_request.version;
        auto& method = http_request.method;
        std::transform(method.begin(),method.end(),method.begin(),::toupper);
    }

   
    void ParseHttpRequestHeader()
    {
      for(auto& iter: http_request.request_header)
      {
        std::string key;
        std::string value;
        if(Util::CutString(iter,key,value,SEP)){
          http_request.header_kv.insert({key,value});
        }
      }
    }
    
    bool  IsNeedRecvHttpRequestBody()
    {
      auto& method = http_request.method;
      if(method == "POST"){
         auto& header_kv = http_request.header_kv;
         auto iter = header_kv.find("Content-Length");
           if(iter != header_kv.end()){
             LOG(INFO,"Post Method, Content-Length:"+iter->second);
             http_request.content_length = atoi(iter->second.c_str());
             return true;
           }
      }
      return false;
    }
    bool  RecvHttpRequestBody()
    {
      if(IsNeedRecvHttpRequestBody()){
        int content_length = http_request.content_length;
        auto& body = http_request.request_body;
        char ch = 0;
        while(content_length){
          ssize_t s = recv(sock,&ch,1,0);
          if(s > 0){
            body.push_back(ch);
            content_length--;
          }
          else{
            stop = true;
            break;
          }
        }
        LOG(INFO,body);
      }
      return stop;
    }
     int ProcessCgi()
     {
        LOG(INFO,"process cgi method!");
        int code = OK;
        //父进程数据
        auto& method = http_request.method;
        auto& query_string = http_request.query_string; //GET
        auto& body_text = http_request.request_body; //POST        
        auto& bin = http_request.path;//子进程执行的目标程序,一点存在
        size_t content_length = http_request.content_length;
        auto& response_body = http_response.response_body;

				std::string query_string_env;				
        std::string method_env;
        std::string content_length_env;

        //父进程角度
        int input[2];
        int output[2];
        
        if(pipe(input) < 0){
          LOG(ERROR,"pipe input error");
          code = SERVER_ERROR;
          return code;
        }
        if(pipe(output) < 0){
          LOG(ERROR,"pipe output error");
          code = SERVER_ERROR;
          return code;
        }
 
       //新线程，但是从头到尾都只有一个进程，就是httpserver!
       pid_t pid = fork();
       if(pid == 0){
         close(input[0]);
         close(output[1]);

				 
         method_env = "METHOD=";
         method_env += method;
         
         putenv((char*)method_env.c_str());

				 if(method == "GET"){
							query_string_env = "QUERY_STRING=";
              query_string_env += query_string;
							putenv((char*)query_string_env.c_str());		
              LOG(INFO,"Get Method, Add Query_String Env");
				}         
         else if(method == "POST"){
            content_length_env = "CONTENT_LENGTH=";
            content_length_env += std::to_string(content_length);
            putenv((char*)content_length_env.c_str());
            LOG(INFO,"Post Method, Add Content_length Evn");
         }
         else{
           //DO nothing
         }
         std::cout << "bin: " << bin << std::endl; 
         //input[1]:写入  -> 1 -> input[1]
         //output[0]:读入  ->0 -> output[0]
         dup2(input[1],1);
         dup2(output[0],0);

         //child -> exec*
         execl(bin.c_str(),bin.c_str(),nullptr);
         //替换成功之后，目标子进程如何得知，对应的读写文件描述符是多少呢？ execl执行前定向到0和1
         exit(1);
       } 
       else if(pid < 0){
         //创建失败
         LOG(ERROR,"fork errro!");
         return 404;
       }
       else{
         //parent
         close(input[1]);
         close(output[0]);
         
         if(method == "POST"){
           const char* start = body_text.c_str();
           size_t total = 0;
           size_t size = 0;
           while(total <  content_length && (size=write(output[1],start+total,body_text.size()-total))>0){
               total += size;
              }
          }
          
          //获取子进程输出的结果
         char ch = 0;
         while(read(input[0],&ch,1) > 0){
            response_body.push_back(ch);
         }
         
         int status = 0;
         pid_t ret =  waitpid(pid,&status,0);
         if(ret == pid){
           if(WIFEXITED(status)){ //子进程正常退出
             if(WEXITSTATUS(status) == 0){ //子进程退出码是正常的
                code = OK;
             }
             else{
               code = BAD_REQUEST;
             }
           }
           else{
             code = SERVER_ERROR;
           }
         } 

         close(input[0]);
         close(output[1]);
       }
       return code;
     }
    void HandlerError(std::string page)
     {
       http_request.cgi = false;
       //给用户返回404页面
       http_response.fd = open(page.c_str(),O_RDONLY);
       if(http_response.fd > 0){
         struct stat st;
         stat(page.c_str(),&st);
         http_request.size = st.st_size;

         std::string line = "Content-Type: text/html";
         line += LINE_END;
         http_response.response_head.push_back(line);


         line = "Content-Length: ";
         line += std::to_string(st.st_size);
         line += LINE_END;
         http_response.response_head.push_back(line);

       }
     }

      int  ProcessNonCgi()
      {
        http_response.fd = open(http_request.path.c_str(),O_RDONLY);
        if(http_response.fd >= 0 ){
          LOG(INFO,http_request.path+ " open succuess!");
          return OK;
        }
        return NOT_FOUND;
      }
      void BuildOkResponse()
      {
         std::string line = "Content-Type: ";
         line += Suffxi2Desc(http_request.suffxi);
         line += LINE_END;
         http_response.response_head.push_back(line);

         line = "Content-Length: ";
         if(http_request.cgi){
            line += std::to_string(http_response.response_body.size());
         }
         else{
            line += std::to_string(http_request.size); //GET
         }
         line += LINE_END;
         http_response.response_head.push_back(line);

      }

     void  BuildHttpResponseHelper()
     {
        //http_requst;
        //http_response;
        auto& code = http_response.status_code;

        //构建状态行
        auto& status_line = http_response.status_line;
        status_line += HTTP_VERSION;
        status_line += " ";
        status_line +=std::to_string(code);
        status_line += " ";
        status_line += Code2Desc(code);
        status_line += LINE_END;
       
        //构建响应正文,可能包括响应报头
        std:: string path = WEB_ROOT;
        path += "/";
        switch(code){
          case OK:
            BuildOkResponse();
            break;
          case NOT_FOUND:
            path += PAGE_404;
            HandlerError(path);
            break;
           case BAD_REQUEST:
            path += PAGE_404;
            HandlerError(path);
            break;
           case SERVER_ERROR:
            path += PAGE_404;
            HandlerError(path);
            break;
     //     case 500:
     //       HanlerError(PAGE_500);
          default:
            break;
        }
     }

  public:
    EndPoint(int _sock):sock(_sock),stop(false)
    {}
    bool IsStop()
    {
      return stop;
    }
    
    //读取请求
    void RcvHttpRequest()
    {
      if(!RecvHttpRequestLine() && !RecvHttpRequestHeader()){
        //成功拿到请求行和请求报头
         ParseHttpRequestLine();
         ParseHttpRequestHeader();
         RecvHttpRequestBody();
      } 
    }


    //构建响应
    void BuildHttpResponse()
    {
      //请求已经全部读完，即可以直接构建响应
      std::string _path;
      struct stat st;
      std::size_t found = 0;
      auto& code = http_response.status_code;
      if(http_request.method != "GET" &&  http_request.method != "POST"){
        //非法请求
        LOG(WARING,"method is not right");
        code = NOT_FOUND;
        goto END;
      }
      
      if(http_request.method == "GET"){
        size_t pos = http_request.uri.find("?"); 
        if(pos != std::string::npos){
          Util::CutString(http_request.uri,http_request.path,http_request.query_string,"?");
          http_request.cgi = true; //url带参
        }
        else{
          http_request.path = http_request.uri;
        }
      }
      else if(http_request.method == "POST"){
        //POST
        http_request.cgi = true;
        http_request.path = http_request.uri;

      }
      else{
        //do nothing
      } 
      _path = http_request.path;
      http_request.path = WEB_ROOT;
      http_request.path += _path;
      if(http_request.path[http_request.path.size()-1] == '/'){
        http_request.path +=HOME_PAGE;
      }
      if(stat(http_request.path.c_str(),&st) == 0){
        //资源存在
        if(S_ISDIR(st.st_mode)){
          //说明请求的资源是一个目录，不被允许的，需要相关处理
          //虽然是一个目录，但是绝对不会以/结尾
          http_request.path += "/";
          http_request.path+=HOME_PAGE;
          stat(http_request.path.c_str(),&st);
        } 
        if((st.st_mode & S_IXUSR) || (st.st_mode& S_IXGRP)||(st.st_mode& S_IXOTH)){
          //特殊处理
          http_request.cgi = true;
        }
        http_request.size = st.st_size;
      }
      else{
        //资源不存在
        LOG(WARING,http_request.path+" Not Found");
        code = NOT_FOUND;
        goto END;
      }
      
      //path
      found = http_request.path.rfind(".");
      if(found == std::string::npos){
        http_request.suffxi = ".html";
      }
      else{
        http_request.suffxi = http_request.path.substr(found);
      }

      if(http_request.cgi){
         
         code = ProcessCgi(); //执行目标程序,结果放到response_body
           
      }
      else{
        //1.目标网页一定存在
        //2.返回并不是单单返回网页，而是要构建http响应
        code =  ProcessNonCgi(); //简单的网页返回，返回静态网页，只需要打开即可
      
      }

      
END:
      BuildHttpResponseHelper();//状态行，响应报头，空行也有了，正文也有了
     
      
    }

    //发送响应
    void SendHttpResponse()
    {
       send(sock,http_response.status_line.c_str(),http_response.status_line.size(),0);
       for(auto iter : http_response.response_head){
          send(sock,iter.c_str(),iter.size(),0);
       }
       send(sock,http_response.blank.c_str(),http_response.blank.size(),0);
       if(http_request.cgi){ //fd response_body
          auto& response_body = http_response.response_body; 
          size_t size = 0;
          size_t total = 0;
          const char* start = response_body.c_str();
          while(total < response_body.size() && (size=send(sock,start+total,response_body.size()-total,0)> 0)){
            total += size;
          }
        } 
       else{
         sendfile(sock,http_response.fd,nullptr,http_request.size);
         close(http_response.fd);
       }

    }
      
    ~EndPoint()
    {
      close(sock);
    }

};



class CallBack{
    public:
      CallBack()
      {}
      void operator()(int sock)
      {
        HandlerRequest(sock);
      }

      void  HandlerRequest(int sock){
        LOG(INFO,"Handler Request Begin");
  
#ifdef DEBUG

    char buffer[4096];
    recv(sock,buffer,sizeof(buffer),0);
    std:: cout << "----------------begin-----------" << std::endl;
    std::cout << buffer << std::endl;
    std:: cout << "----------------end-----------" << std::endl;
#else
          EndPoint* ep = new EndPoint(sock);
          ep->RcvHttpRequest();
          if(!ep->IsStop()){
             LOG(INFO,"Recv No Error, Begin Build And Send ");
             ep->BuildHttpResponse();
             ep->SendHttpResponse();
          }
          else{
            LOG(WARING,"Recv Error, Stop Build And Send");
          }
          delete ep;
#endif
        LOG(INFO,"Hander Request End");
    }
    ~CallBack()
    {}
};
